/*
 ------------------------------------------------------------------------------
 Copyright (C) 2013 Eternal Games.

 This file is part of the GLQuake source code.

 The GLQuake source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The GLQuake source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the GLQuake source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


//
// sg_utils.cpp - miscellaneous utility functions
//


#include "sg_local.h"


/*
 ==================
 SG_IsEntityNameValid
 ==================
*/
static bool SG_IsEntityNameValid (const char *name){

	int		i;

	if (!name || !name[0])
		return false;

	if (Str_Length(name) >= MAX_ENTITY_NAME_LENGTH)
		return false;

	for (i = 0; name[i]; i++){
		if (name[i] >= 'a' && name[i] <= 'z')
			continue;
		if (name[i] >= 'A' && name[i] <= 'Z')
			continue;
		if (name[i] == '_')
			continue;

		if (i != 0){
			if (name[i] >= '0' && name[i] <= '9')
				continue;
		}

		return false;
	}

	return true;
}

/*
 ==================
 SG_IsEntityNameUnique
 ==================
*/
static bool SG_IsEntityNameUnique (const char *name, int entityNum){

	etEntity	*check;
	int			i;

	if (!name || !name[0])
		return false;

	for (i = 0, check = level.entities; i < level.numEntities; i++, check++){
		if (!check->inUse)
			continue;

		if (check->state.number == entityNum)
			continue;

		if (!Str_ICompare(name, check->name))
			return false;
	}

	return true;
}

/*
 ==================
 SG_ValidateEntityName
 ==================
*/
const char *SG_ValidateEntityName (const char *name, const char *className, int entityNum){

	static char	generated[MAX_ENTITY_NAME_LENGTH];
	int			i;

	// Make absolutely sure it has a valid and unique name
	if (!Str_ICompare(className, "worldSpawn"))
		return className;

	if (!SG_IsEntityNameValid(name) || !SG_IsEntityNameUnique(name, entityNum)){
		// Generate a unique name
		for (i = 0; i < MAX_ENTITIES; i++){
			Str_SPrintf(generated, sizeof(generated), "%s_%i", className, i+1);

			if (SG_IsEntityNameUnique(generated, entityNum))
				return generated;
		}

		// Should never happen
		G_Error("SG_ValidateEntityName: could not find a unique name for entity #%i", entityNum);
	}

	return name;
}


// ============================================================================


/*
 ==================
 SG_AddEntityToHashTable
 ==================
*/
void SG_AddEntityToHashTable (etEntity *entity){

	uint	hashKey;

	hashKey = Str_HashKey(entity->name, ENTITIES_HASH_SIZE, false);

	entity->nextHash = level.entitiesHashTable[hashKey];
	level.entitiesHashTable[hashKey] = entity;
}

/*
 ==================
 SG_RemoveEntityFromHashTable
 ==================
*/
void SG_RemoveEntityFromHashTable (etEntity *entity){

	etEntity	*hash, **prevHash;
	uint		hashKey;

	hashKey = Str_HashKey(entity->name, ENTITIES_HASH_SIZE, false);

	prevHash = &level.entitiesHashTable[hashKey];

	while (1){
		hash = *prevHash;
		if (!hash)
			break;		// Not found

		if (!Str_ICompare(hash->name, entity->name)){
			*prevHash = hash->nextHash;
			break;
		}

		prevHash = &hash->nextHash;
	}
}

/*
 ==================
 SG_FindEntity

 Returns the entity whose name matches the specified string
 ==================
*/
etEntity *SG_FindEntity (const char *name){

	etEntity	*entity;
	uint		hashKey;

	hashKey = Str_HashKey(name, ENTITIES_HASH_SIZE, false);

	for (entity = level.entitiesHashTable[hashKey]; entity; entity = entity->nextHash){
		if (!Str_ICompare(entity->name, name))
			return entity;
	}

	return NULL;
}


// ============================================================================


/*
 ==================
 SG_AllocEntity

 Either finds a free entity, or allocates a new one.

 The slots from 0 to MAX_CLIENTS - 1 are always reserved for clients, and will
 never be used by anything else.

 Try to avoid reusing an entity that was recently freed, because it can cause
 the clients to think the entity morphed into something else instead of being
 removed and recreated.
 ==================
*/
etEntity *SG_AllocEntity (const char *name, const char *className, const etDict *dict){

	etEntity	*entity;
	int			i;

	// Try to find a free slot
	for (i = MAX_CLIENTS; i < level.numEntities; i++){
		entity = &level.entities[i];

		if (!entity->inUse)
			break;
	}

	// If we didn't find a free slot, allocate a new one
	if (i == level.numEntities){
		if (level.numEntities == ENTITYNUM_MAX_NORMAL)
			G_Error("SG_AllocEntity: MAX_ENTITIES hit");

		entity = &level.entities[level.numEntities++];
	}

	// Validate the name
	name = SG_ValidateEntityName(name, className, i);

	// Set up the entity
	entity->Setup(i, ET_BAD, name, className, dict);

	return entity;
}

/*
 ==================
 SG_FreeEntity

 May defer freeing the entity until the end of the frame
 ==================
*/
void SG_FreeEntity (etEntity *entity){

	if (!entity->inUse)
		return;

	if (!level.allowFrees){
		entity->deferredFree = true;
		return;
	}

	// Clear the entity
	entity->Clear();
}

/*
 ==================
 SG_ActuallyFreeEntities

 Actually frees any entities marked by SG_FreeEntity
 ==================
*/
void SG_ActuallyFreeEntities (){

	etEntity	*entity;
	int			i;

	level.allowFrees = true;

	// Commit any deferred frees. This may also free other entities.
	for (i = 0, entity = level.entities; i < level.numEntities; i++, entity++){
		if (!entity->inUse)
			continue;

		if (!entity->deferredFree)
			continue;

		// Clear the entity
		entity->Clear();
	}

	// Any future calls to SG_FreeEntity will be deferred until the end of the
	// next frame
	level.allowFrees = false;
}


// ============================================================================


/*
 ==================
 SG_AngleToDir

 The editor only specifies a single value (yaw), but we have special constants
 to generate an up or down direction
 ==================
*/
void SG_AngleToDir (float angle, glqVec3 &dir){

	if (angle == -1.0f){
		dir.Set(0.0f, 0.0f, 1.0f);
		return;
	}

	if (angle == -2.0f){
		dir.Set(0.0f, 0.0f, -1.0f);
		return;
	}

	dir = glqAngles(0.0f, angle, 0.0f).ToForward();
}

/*
 ==================
 
 Kills all entities that would touch the proposed new positioning of the entity
 ==================
*/
void SG_KillBox (etEntity *entity){

}

/*
 ==================
 
 ==================
*/
void SG_RadiusDamage (){

}